use std::{
    collections::HashMap,
    fs,
    path::PathBuf,
    sync::{Arc, Mutex},
};

use clap::Parser;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use walkdir::WalkDir;

/// 命令行参数
#[derive(Parser, Debug, Clone)]
struct Cli {
    #[arg(help = "要检查的目录")]
    dir: String,

    #[arg(short, long, help = "要排除的目录或文件")]
    exclude: Vec<String>,

    #[arg(short, long, help = "检查源文件是否存在", default_value_t = false)]
    check_sourcefile_exist: bool,

    #[arg(long, help = "自动删除源文件不存在的 meta 文件", default_value_t = false, requires = "check_sourcefile_exist")]
    auto_delete: bool,
}

fn main() {
    let args = Cli::parse();
    let meta_paths: Vec<PathBuf> = WalkDir::new(&args.dir)
        .into_iter()
        .filter_map(|entry| entry.ok()) // 忽略错误的条目
        .filter(|entry| {
            let path = entry.path();

            let path_str = match path.to_str() {
                Some(s) => s,
                None => {
                    return false;
                }
            };

            let is_excluded = args.exclude.iter().any(|ex| path_str.contains(ex));
            !is_excluded && path_str.ends_with(".meta") && entry.file_type().is_file()
        })
        .map(|entry| entry.into_path())
        .collect();

    let uuid_map = Arc::new(Mutex::new(HashMap::<String, Vec<PathBuf>>::new()));
    let source_file_not_found = Arc::new(Mutex::new(Vec::<PathBuf>::new()));

    meta_paths.par_iter().for_each(|meta_path| {
        process_meta_file(meta_path, uuid_map.clone());
        if args.check_sourcefile_exist {
            check_source_file_exist(meta_path, source_file_not_found.clone());
        }
    });

    let map_snapshot = uuid_map.lock().unwrap();
    for (uuid, files) in map_snapshot.iter() {
        if files.len() > 1 {
            println!("检测到重复 UUID: {}", uuid);
            for f in files {
                println!("    {}", f.display());
            }
        }
    }

    if args.check_sourcefile_exist {
        let not_found_snapshot = source_file_not_found.lock().unwrap();
        for f in not_found_snapshot.iter() {
            println!("源文件不存在: {}", f.display());
            if args.auto_delete {
                if let Err(e) = fs::remove_file(f) {
                    eprintln!("删除文件失败: {}", e);
                } else {
                    println!("已删除");
                }
            }
        }
    }
}

fn check_source_file_exist(meta_path: &PathBuf, source_file_not_found: Arc<Mutex<Vec<PathBuf>>>) {
    let Some(path_str) = meta_path.to_str() else {
        return;
    };

    let Some(source_str) = path_str.strip_suffix(".meta") else {
        return;
    };

    let source_path = PathBuf::from(source_str);

    if !source_path.exists() {
        let mut guard = source_file_not_found.lock().unwrap();
        guard.push(meta_path.clone());
    }
}

fn process_meta_file(meta_path: &PathBuf, uuid_map: Arc<Mutex<HashMap<String, Vec<PathBuf>>>>) {
    let content = match fs::read_to_string(meta_path) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("读取文件失败 ({}): {}", meta_path.display(), e);
            return;
        }
    };

    let json_val: serde_json::Value = match serde_json::from_str(&content) {
        Ok(val) => val,
        Err(e) => {
            eprintln!("解析 JSON 失败 ({}): {}", meta_path.display(), e);
            return;
        }
    };

    if let Some(uuid_str) = json_val.get("uuid").and_then(|v| v.as_str()) {
        let mut map_guard = uuid_map.lock().unwrap();
        map_guard
            .entry(uuid_str.to_string())
            .or_default()
            .push(meta_path.clone());
    } else {
        eprintln!("未找到 uuid 字段 ({})", meta_path.display());
    }
}
